#include "3DSLoader.h"
#include <stdlib.h>
#include <string.h>
#include "fmtxlib.h"

using namespace std;

Load3DS::Load3DS()
{
	m_currentChunk = NULL;
	m_tempChunk = new Chunk;
	m_meshes = NULL;
}


Load3DS::~Load3DS()
{
	delete m_tempChunk;
}

bool Load3DS::ReadChunk(Chunk* a_chunk) 
{
	m_file.read(reinterpret_cast<char*>(&a_chunk->m_ID), sizeof(unsigned short) );
	m_file.read(reinterpret_cast<char*>(&a_chunk->m_length), sizeof(unsigned long) );
	a_chunk->m_readBytes = sizeof(unsigned long) + sizeof(unsigned long);
	return true;
}

std::string Load3DS::ReadString(Chunk* a_chunk)
{
	const unsigned long c_nameLen = 64;
	char				objName[c_nameLen] = {0};
	unsigned long		num = 0;
	char				c = 'z';

	while(c != '\0') {
		m_file.read(&c, 1);
		if(num < c_nameLen) objName[num] = c;
		++num;
	}
	objName[c_nameLen - 1] = '\0';
	c = '\0';
	a_chunk->m_readBytes += num;
	return std::string(objName);
}

void Load3DS::MoveToEndOfChunkData(Chunk* a_chunk)
{
	m_file.seekp(a_chunk->m_length - 6, ios::cur);
	a_chunk->m_readBytes += a_chunk->m_length - 6;
}


int Load3DS::Load(const wchar_t * a_filename, std::list<Mesh*>* a_meshes)
{
	int lBytesRead = 0;
	if(a_filename == NULL || a_meshes == NULL) return BAD_ARG;
	m_meshes = a_meshes;

	m_file.open(a_filename, ios::in | ios::binary);
	if( !m_file.is_open() )
		return CANT_OPEN_FILE;

	m_currentChunk = new Chunk();
	ReadChunk(m_currentChunk);
	if(m_currentChunk->m_ID != PRIMARY)
		return BAD_FILE_FORMAT;

	ProcessNextChunk(m_currentChunk);

	std::list<Mesh*> listTmp;
	listTmp.swap(*m_meshes);
	m_meshes->clear();
	std::list<Mesh*>::iterator iter = listTmp.begin();
	while(iter != listTmp.end()) {
		if( (*iter)->m_triangles.size() != 0 )
			m_meshes->push_back(*iter);
		++iter;
	}
	listTmp.clear();

	m_file.close();
	return ALL_OK;
}

int Load3DS::ProcessNextChunk(Chunk* a_prevChunk)
{
	Chunk temp;
	Mesh* curMesh = NULL;
	m_currentChunk = new Chunk();

	while( a_prevChunk->m_readBytes < a_prevChunk->m_length ) {
		ReadChunk(m_currentChunk);

		switch(m_currentChunk->m_ID) {
			case EDIT3DS:
				ReadChunk(&temp);
				MoveToEndOfChunkData(&temp);
				m_currentChunk->m_readBytes += temp.m_readBytes;
				ProcessNextChunk(m_currentChunk);
				break;
			case EDITOBJECT:
				curMesh = new Mesh();
				m_meshes->push_back(curMesh);
				curMesh->m_name = ReadString(m_currentChunk);

				ProcessNextObjectChunk(m_currentChunk, curMesh);
				break;
			default:
				MoveToEndOfChunkData(m_currentChunk);
				break;
		}
		a_prevChunk->m_readBytes += m_currentChunk->m_readBytes;
	}
	delete m_currentChunk;
	m_currentChunk = a_prevChunk;
	return 0;
}

int Load3DS::ProcessNextObjectChunk(Chunk *a_prevChunk, Mesh* a_mesh)
{
	//Mesh* curMesh = NULL;
	m_currentChunk = new Chunk();

	while( a_prevChunk->m_readBytes < a_prevChunk->m_length ) {
		ReadChunk(m_currentChunk);

		switch(m_currentChunk->m_ID) {
			case OBJTRIMESH:
				//curMesh = new Mesh();
				//m_meshes->push_back(curMesh);
				//curMesh->m_name = a_mesh->m_name;
				ProcessNextObjectChunk(m_currentChunk, a_mesh);
				ComputeNormalInMesh(a_mesh);
				break;
			case TRIVERT:
				FillVertexBuffer(m_currentChunk, a_mesh);
				break;
			case TRIFACE:
				FillIndexBuffer(m_currentChunk, a_mesh);
				break;
			default:
				MoveToEndOfChunkData(m_currentChunk);
				break;
		}
		a_prevChunk->m_readBytes += m_currentChunk->m_readBytes;
	}
	delete m_currentChunk;
	m_currentChunk = a_prevChunk;
	return 0;
}

void Load3DS::FillIndexBuffer(Chunk *a_chunk, Mesh *a_mesh)
{
	unsigned short num;
	unsigned short siTmp[4];
	num = 0;
	m_file.read(reinterpret_cast<char*>(&num), 2);
	a_chunk->m_readBytes += 2;
	if(num == 0) return;
	a_mesh->m_triangles.resize(num);
	for(unsigned short i=0; i < num; ++i) {
		m_file.read(reinterpret_cast<char*>(siTmp), 8);
		a_mesh->m_triangles[i].m_normals[0] = siTmp[0];
		a_mesh->m_triangles[i].m_normals[1] = siTmp[1];
		a_mesh->m_triangles[i].m_normals[2] = siTmp[2];
		a_mesh->m_vertices[siTmp[0]].m_owners.push_back(i);
		a_mesh->m_vertices[siTmp[1]].m_owners.push_back(i);
		a_mesh->m_vertices[siTmp[2]].m_owners.push_back(i);
		a_chunk->m_readBytes += 8;
	}
}

void Load3DS::FillVertexBuffer(Chunk *a_chunk, Mesh *a_mesh)
{
	unsigned short num;
	num = 0;
	m_file.read(reinterpret_cast<char*>(&num), 2);
	a_chunk->m_readBytes += 2;
	if(num == 0) return;
	a_mesh->m_vertices.resize(num);
	a_mesh->m_normals.resize(num);
	for(unsigned short i=0; i < num; ++i) {
		m_file.read(reinterpret_cast<char*>(a_mesh->m_vertices[i].m_position), 12);
		a_mesh->m_vertices[i].m_myMesh = a_mesh;
		a_chunk->m_readBytes += 12;
		a_mesh->m_normals[i].m_vertexNum = i;
		memset(a_mesh->m_normals[i].m_normal, 0, 3*sizeof(float));
		a_mesh->m_bbox.EnlargeBBox(a_mesh->m_vertices[i].m_position);
	}
}

int Load3DS::ComputeNormalInMesh(Mesh* a_mesh)
{
	fmath::vector3 v1, v2, normal;
	for(unsigned long i = 0; i < a_mesh->m_triangles.size(); ++i) {
		v1.x = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[0]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_vertexNum].m_position[0];
		v1.y = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[1]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_vertexNum].m_position[1];
		v1.z = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[2]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_vertexNum].m_position[2];

		v2.x = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_vertexNum].m_position[0]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[0];
		v2.y = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_vertexNum].m_position[1]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[1];
		v2.z = a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_vertexNum].m_position[2]
			- a_mesh->m_vertices[a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_vertexNum].m_position[2];

		normal = fmath::CrossProduct(v1, v2);

		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_normal[0] += normal.x;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_normal[1] += normal.y;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[0]].m_normal[2] += normal.z;

		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_normal[0] += normal.x;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_normal[1] += normal.y;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[1]].m_normal[2] += normal.z;

		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_normal[0] += normal.x;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_normal[1] += normal.y;
		a_mesh->m_normals[a_mesh->m_triangles[i].m_normals[2]].m_normal[2] += normal.z;
	}

	for(unsigned long i = 0; i < a_mesh->m_normals.size(); ++i) {
		normal.x = a_mesh->m_normals[i].m_normal[0];
		normal.y = a_mesh->m_normals[i].m_normal[1];
		normal.z = a_mesh->m_normals[i].m_normal[2];
		normal = fmath::Normalized(normal);
		a_mesh->m_normals[i].m_normal[0] = normal.x;
		a_mesh->m_normals[i].m_normal[1] = normal.y;
		a_mesh->m_normals[i].m_normal[2] = normal.z;
	}
	return 0;
}
/*
Chunk::Chunk()
{
	m_ID = 0;
	m_length = 0;	
}


Chunk::~Chunk()
{
}
*/